﻿using MethodBoundaryAspect.Fody.Attributes;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;

namespace Seagull.Aop
{
    /// <summary>
    /// 事务拦截
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class TransactionScopeAttribute : OnMethodBoundaryAspect
    {
        private readonly ILogger? _logger;
        private readonly TransactionScopeAsyncFlowOption _asyncFlowOption;

        /// <summary>
        /// 使用默认的方式构建事务对象
        /// </summary>
        public TransactionScopeAttribute() : this(TransactionScopeAsyncFlowOption.Suppress)
        {
        }

        /// <summary>
        /// 使用指定的方式构建事务对象
        /// </summary>
        /// <param name="asyncFlowOption"></param>
        public TransactionScopeAttribute(TransactionScopeAsyncFlowOption asyncFlowOption)
        {
            this._asyncFlowOption = asyncFlowOption;
            if (App._provider is not null)
            {
                _logger = App.HttpContext.RequestServices.GetService<ILogger<TransactionScopeAttribute>>();
            }
        }

        public override void OnEntry(MethodExecutionArgs args)
        {
            if (_logger is not null)
            {
                _logger?.LogDebug($"{args.Instance.GetType().Name}.{args.Method.Name}开启事务...");
            }
            args.MethodExecutionTag = new TransactionScope(this._asyncFlowOption);
        }

        public override void OnExit(MethodExecutionArgs args)
        {
            //如果返回的是task对象，由于task的异步性，需要对args.ReturnValue进行替换，以确定代码的正确执行顺序
            if (args.ReturnValue is Task t)
            {
                MethodInfo method = (MethodInfo)args.Method;
                if (method.GetCustomAttribute<AsyncStateMachineAttribute>() is not null && method.ReturnType.IsGenericType)
                {
                    Type type = method.ReturnType.GenericTypeArguments[0];
                    args.ReturnValue = this.GetType().GetMethod(nameof(BuildGenericMethod), BindingFlags.NonPublic | BindingFlags.Instance)!.MakeGenericMethod(type).Invoke(this, new object[] { t, args });
                }
                else
                {
                    args.ReturnValue = t.ContinueWith(task => Exit(args));
                }
            }
            else
            {
                Exit(args);
            }
        }

        protected virtual void Exit(MethodExecutionArgs args)
        {
            try
            {
                var transactionScope = (TransactionScope)args.MethodExecutionTag;
                if (args.Exception is null)
                {
                    transactionScope.Complete();
                }
                transactionScope.Dispose();
                _logger?.LogDebug($"{args.Instance.GetType().Name}.{args.Method.Name}提交事务...");
            }
            catch (Exception ex)
            {
                _logger?.LogError($"{args.Instance.GetType().Name}.{args.Method.Name}事务异常：{ex.StackTrace}");
                throw;
            }
        }

        private Task<T> BuildGenericMethod<T>(Task<T> task, MethodExecutionArgs args)
        {
            return task.ContinueWith(t =>
            {
                Exit(args);
                return t.Result;
            });
        }

        public override void OnException(MethodExecutionArgs args)
        {
            var transactionScope = (TransactionScope)args.MethodExecutionTag;
            transactionScope.Dispose();
        }
    }
}
